#ifndef PROMOTE_ARRAY_LIST_H_
#define PROMOTE_ARRAY_LIST_H_

#include <assert.h>
#include <iso646.h>
#include <stddef.h>
#include <stdbool.h>

#include "universal.h"

typedef struct pmt_arraylist {
    pmt_universal_t* array;
    size_t front;
    size_t size;
    size_t capacity;
    size_t capacity_log2;
    size_t mask;
} pmt_arraylist_t;

/* Init/Uninit */
void 
pmt_arraylist_init(pmt_arraylist_t* const restrict list,
                   size_t const min_init_capacity);

void 
pmt_arraylist_uninit(pmt_arraylist_t* const restrict list);

/* Properties */
static inline bool
pmt_arraylist_is_empty(pmt_arraylist_t const* const restrict list)
{
    return list->size == 0;
}

static inline size_t
pmt_arraylist_size(pmt_arraylist_t const* const restrict list)
{
    return list->size;
}

/* Observation */
static inline pmt_universal_t
pmt_arraylist_back(pmt_arraylist_t const* const restrict list)
{
    assert(list->size);
    return list->array[(list->front + list->size - 1) & list->mask];
}

static inline pmt_universal_t
pmt_arraylist_front(pmt_arraylist_t const* const restrict list)
{
    assert(list->size);
    return list->array[list->front & list->mask];
}

static inline pmt_universal_t
pmt_arraylist_get(pmt_arraylist_t const* const restrict list,
                  size_t const index)
{
    assert(index < list->size);
    return list->array[(list->front + index) & list->mask];
}

/* Modify */
bool
pmt_arraylist_ensure_capacity(pmt_arraylist_t* const restrict list,
                              size_t const min_capacity);

static inline bool
pmt_arraylist_add_back(pmt_arraylist_t* const restrict list,
                       pmt_universal_t const item)
{
    if( list->size < list->capacity or pmt_arraylist_ensure_capacity(list, list->capacity << 1) ) {
        list->array[(list->front + (list->size)++) & list->mask] = item;
        return true;
    } else {
        return false;
    }
}

static inline bool
pmt_arraylist_add_front(pmt_arraylist_t* const restrict list,
                        pmt_universal_t const item)
{
    if( list->size < list->capacity or pmt_arraylist_ensure_capacity(list, list->capacity << 1) ) {
        list->array[list->front = ((list->front - 1) & list->mask)] = item;
        ++(list->size);
        return true;
    } else {
        return false;
    }
}


/*static inline pmt_universal_t
pmt_arraylist_remove_index(pmt_arraylist_t* const restrict list,
                           size_t const index)
{
    assert(false);
}*/

static inline void
pmt_arraylist_set(pmt_arraylist_t* const restrict list,
                  size_t const index,
                  pmt_universal_t const item)
{
    assert(index < list->size);
    list->array[(list->front + index) & list->mask] = item;
}

#endif /* PROMOTE_ARRAY_LIST_H_ */

